#pragma once

#include "Callback.h"
#include "ValueKey.h"

namespace AT
{

class IAsyncTask
{
public:
	virtual bool Init() = 0;
	virtual void Exit() = 0;
	virtual bool AsyncTask(ICallbackPtr&& pTask) = 0;
	virtual bool OrderedTask(CValueKey key, ICallbackPtr&& pTask) = 0;
	virtual bool DelayedTask(UINT nDelayMs, ICallbackPtr&& pTask) = 0;
	virtual bool UIAsyncTask(ICallbackPtr&& pTask) = 0;
	virtual bool UIDelayedTask(UINT nDelayMs, ICallbackPtr&& pTask) = 0;
};

IAsyncTask *GetAsyncTask();

// Threadpool task, returns the task id on success, 0 on failure
template<typename Fn, typename... Args>
inline bool AsyncTask(Fn&& fn, Args&&... args)
{
	return GetAsyncTask()->AsyncTask(MakeCallback(std::forward<Fn>(fn), std::forward<Args>(args)...));
}

// Threadpool ordered task, returns the task id on success, 0 on failure
// The type of the key can be pointer, integer and enum
template<typename Fn, typename... Args>
inline bool OrderedTask(CValueKey key, Fn&& fn, Args&&... args)
{
	return GetAsyncTask()->OrderedTask(key, MakeCallback(std::forward<Fn>(fn), std::forward<Args>(args)...));
}

// Threadpool delayed task, returns the task id on success, 0 on failure
template<typename Fn, typename... Args>
inline bool DelayedTask(UINT nDelayMs, Fn&& fn, Args&&... args)
{
	return GetAsyncTask()->DelayedTask(nDelayMs, MakeCallback(std::forward<Fn>(fn), std::forward<Args>(args)...));
}

// UI thread task, returns the task id on success, 0 on failure
template<typename Fn, typename... Args>
inline bool UIAsyncTask(Fn&& fn, Args&&... args)
{
	return GetAsyncTask()->UIAsyncTask(MakeCallback(std::forward<Fn>(fn), std::forward<Args>(args)...));
}

// UI thread delayed task, returns the task id on success, 0 on failure
template<typename Fn, typename... Args>
inline bool UIDelayedTask(UINT nDelayMs, Fn&& fn, Args&&... args)
{
	return GetAsyncTask()->UIDelayedTask(nDelayMs, MakeCallback(std::forward<Fn>(fn), std::forward<Args>(args)...));
}

#ifdef _FUTURE_
// Threadpool task, returns the future of the task on success, empty on failure
template<typename Fn, typename... Args>
inline decltype(auto) AsyncFuture(Fn&& fn, Args&&... args)
{
	using _Ret = std::invoke_result_t<std::decay_t<Fn>, std::decay_t<Args>...>;
	std::packaged_task<_Ret(std::decay_t<Args>&&...)> task(std::forward<Fn>(fn));
	auto fut = task.get_future();

	if (!AsyncTask(std::move(task), std::forward<Args>(args)...))
		fut = std::future<_Ret>();

	return fut;
}

// UI thread task, returns the future of the task on success, empty on failure
template<typename Fn, typename... Args>
inline decltype(auto) UIAsyncFuture(Fn&& fn, Args&&... args)
{
	using _Ret = std::invoke_result_t<std::decay_t<Fn>, std::decay_t<Args>...>;
	std::packaged_task<_Ret(std::decay_t<Args>&&...)> task(std::forward<Fn>(fn));
	auto fut = task.get_future();

	if (!UIAsyncTask(std::move(task), std::forward<Args>(args)...))
		fut = std::future<_Ret>();

	return fut;
}
#endif

}	// namespace AT

/*
Other future tasks can also be used like this:

int Add(int a, int b)
{
	return a + b;
}

{
	int x = 3, y = 5;

	// Method 1 (recommend)
	std::packaged_task<int(int, int)> task1(Add);
	auto fut1 = task1.get_future();
	AT::AsyncTask(std::move(task1), x, y);

	// Method 2
	std::packaged_task<int()> task2(std::bind(Add, x, y));
	auto fut2 = task2.get_future();
	AT::AsyncTask(std::move(task2));

	// Method 3
	std::packaged_task<int()> task3([x, y] { return Add(x, y); });
	auto fut3 = task3.get_future();
	AT::AsyncTask(std::move(task3));
}

There is no need for the CancelTask function, if you want to cancel the task, you can do this:
First define a member variable m_pbCanceled, then capture it when posting the task, when you want to cancel the task: *m_pbCanceled = true;

class CTest
{
public:
	CTest()
		: m_pbCanceled(std::make_shared<bool>(false))
	{
	}

	~CTest()
	{
		*m_pbCanceled = true;
	}

	void Func()
	{
		AT::UIDelayedTask(1000, [this, pbCanceled = m_pbCanceled]
		{
			if (*pbCanceled)
				return;
			Task1();
		});

		AT::UIDelayedTask(1000, &CTest::Task2, this, m_pbCanceled);
	}

private:
	void Task1()
	{
		printf("Task1\n");
	}

	void Task2(std::shared_ptr<bool> pbCanceled)
	{
		if (*pbCanceled)
			return;
		printf("Task2\n");
	}

	std::shared_ptr<bool> m_pbCanceled;
};
*/
